feat: add HGS algorithm#2383
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a new preview “Evolutionary Algorithm” phase (Hybrid Genetic Search/HGS-oriented) into Timefold Solver, including XML configuration support, module exports, and a set of supporting population/individual/crossover/state-management abstractions.
Changes:
- Add a new
evolutionaryAlgorithmphase type/config (JAXB + XSD) and wire it into phase creation and preview-feature gating. - Implement core evolutionary building blocks (population/individual representations, construction strategies, crossover strategies, solution state save/restore, best-solution updating).
- Extend/adjust several solver internals to support the new execution mode (thread types, event firing control, logging toggles, nearby selection for sublist moves, assorted test updates).
Reviewed changes
Copilot reviewed 110 out of 110 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/benchmark/src/main/resources/benchmark.xsd | Adds benchmark schema support for the new evolutionary algorithm phase. |
| core/src/main/resources/solver.xsd | Adds solver schema support for configuring the evolutionary algorithm phase. |
| core/src/main/java/ai/timefold/solver/core/config/evolutionaryalgorithm/package-info.java | Declares JAXB XML namespace/schema settings for the new config package. |
| core/src/main/java/ai/timefold/solver/core/config/evolutionaryalgorithm/EvolutionaryAlgorithmPhaseConfig.java | Adds top-level evolutionary algorithm phase configuration model. |
| core/src/main/java/ai/timefold/solver/core/config/evolutionaryalgorithm/EvolutionaryPopulationConfig.java | Adds population sizing/restart configuration model. |
| core/src/main/java/ai/timefold/solver/core/config/evolutionaryalgorithm/EvolutionaryWorkerConfig.java | Adds worker behavior configuration model (exploratory rate, generator, LS). |
| core/src/main/java/ai/timefold/solver/core/config/evolutionaryalgorithm/EvolutionaryIndividualGeneratorConfig.java | Adds configuration for individual generation (phase commands, CH config, properties). |
| core/src/main/java/ai/timefold/solver/core/config/evolutionaryalgorithm/EvolutionaryLocalSearchConfig.java | Adds configuration wrapper for local search used by the evolutionary algorithm. |
| core/src/main/java/ai/timefold/solver/core/config/solver/PreviewFeature.java | Adds EVOLUTIONARY_ALGORITHM preview feature flag. |
| core/src/main/java/ai/timefold/solver/core/config/solver/SolverConfig.java | Registers the new phase config type for JAXB solver config parsing. |
| core/src/main/java/ai/timefold/solver/core/config/phase/PhaseConfig.java | Extends JAXB @XmlSeeAlso to include the new phase config. |
| core/src/main/java/ai/timefold/solver/core/impl/phase/PhaseType.java | Adds EVOLUTIONARY_ALGORITHM to phase type enumeration. |
| core/src/main/java/ai/timefold/solver/core/impl/phase/PhaseFactory.java | Wires phase factory creation to handle EvolutionaryAlgorithmPhaseConfig. |
| core/src/main/java/ai/timefold/solver/core/impl/phase/AbstractPhaseFactory.java | Maps the new phase config to its phase scope type. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/EvolutionaryAlgorithmPhase.java | Adds a marker interface for evolutionary algorithm phases. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/DefaultEvolutionaryAlgorithmPhase.java | Implements the evolutionary algorithm phase loop (load population, evolve, termination). |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/decider/EvolutionaryDecider.java | Defines the contract for evolutionary deciders (load/evolve + lifecycle). |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/decider/AbstractHybridGeneticSearchDecider.java | Adds shared restart/lifecycle logic for HGS-style deciders. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/decider/HybridGeneticSearchDecider.java | Implements HGS decider logic (selection, crossover, restarts, worker setup). |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/decider/HybridGeneticSearchWorkerContext.java | Captures worker strategy/context inputs (construction, crossover, state manager, etc.). |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/population/Population.java | Introduces population contract (add/select/restart/best/stats). |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/population/DefaultPopulation.java | Provides a default population implementation (delegating to abstract base). |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/population/PopulationStatistics.java | Adds population evolution statistics record. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/population/PopulationDiffMap.java | Adds an identity-based diff cache to support diversity/survival selection. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/population/individual/Individual.java | Adds the individual contract (solution/chromosome/diff/score/parents/clone). |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/population/individual/IndividualBuilder.java | Adds a functional interface for building individuals. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/population/individual/ChromosomeEntry.java | Adds chromosome entry representation used by individuals and operators. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/population/individual/AbstractIndividual.java | Adds common implementation for individual score/feasibility/ordering. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/population/individual/BasicVariableIndividual.java | Adds chromosome/diff/clone implementation for basic variables. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/population/individual/ListVariableIndividual.java | Adds chromosome/diff/clone implementation for list variables. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/population/individual/generator/ConstructionIndividualStrategy.java | Adds contract for construction strategies used to generate individuals. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/population/individual/generator/DefaultConstructionIndividualStrategy.java | Adds a default deterministic/shuffled construction strategy for population seeding. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/population/individual/generator/basic/BasicRuinRecreateIndividualStrategy.java | Adds ruin/recreate construction strategy for basic variables. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/population/individual/generator/list/ListRuinRecreateIndividualStrategy.java | Adds ruin/recreate construction strategy for list variables. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/crossover/CrossoverContext.java | Adds crossover execution context (phase scope + two parents). |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/crossover/CrossoverResult.java | Adds crossover result record (offspring + parent score provenance). |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/crossover/CrossoverStrategy.java | Adds crossover contract (apply + phases used). |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/crossover/basic/BasicOXCrossover.java | Implements OX crossover for basic variables. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/crossover/list/AbstractListCrossover.java | Adds shared list crossover helpers, including best-fit insertion logic. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/crossover/list/ListOXCrossover.java | Implements OX crossover for list variables. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/crossover/list/ListRXCrossover.java | Implements RX/SREX-style crossover for list variables. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/common/Utils.java | Adds shared indexing/cut-point utilities for evolutionary operators. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/common/scope/EvolutionaryAlgorithmPhaseScope.java | Adds phase scope for the evolutionary algorithm. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/common/scope/EvolutionaryAlgorithmStepScope.java | Adds step scope for the evolutionary algorithm. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/common/phase/EvolutionaryAlgorithmPhaseLifecycleListener.java | Adds lifecycle listener contract for the new phase type. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/common/bestsolution/BestSolutionUpdater.java | Adds contract for updating best solution from worker progress. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/common/bestsolution/DefaultBestSolutionUpdater.java | Implements a best-solution updater using solution-state transfer. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/common/state/SolutionState.java | Adds solution-state abstraction (solution + score). |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/common/state/SolutionStateManager.java | Adds state save/restore contract used by workers/operators. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/common/state/basic/BasicSolutionState.java | Adds basic-variable solution state record. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/common/state/basic/BasicSolutionStateManager.java | Implements save/restore for basic-variable solutions. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/common/state/basic/BasicValueState.java | Adds per-variable captured state for basic-variable solutions. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/common/state/list/ListSolutionState.java | Adds list-variable solution state record. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/common/state/list/ListSolutionStateManager.java | Implements save/restore for list-variable solutions. |
| core/src/main/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/common/state/list/ListValueState.java | Adds per-value captured state for list-variable solutions. |
| core/src/main/java/ai/timefold/solver/core/impl/solver/thread/ChildThreadType.java | Adds evolutionary-agent child thread type. |
| core/src/main/java/ai/timefold/solver/core/impl/solver/scope/SolverScope.java | Adds best-solution-event trigger flag and evolutionary child-scope initialization. |
| core/src/main/java/ai/timefold/solver/core/impl/solver/recaller/BestSolutionRecaller.java | Gates best-solution event firing on the new solver-scope flag. |
| core/src/main/java/ai/timefold/solver/core/impl/score/director/AbstractScoreDirector.java | Extends child-thread score director creation to support evolutionary threads. |
| core/src/main/java/ai/timefold/solver/core/impl/solver/DefaultSolverFactory.java | Adds constraint-count computation to config policy for evolutionary decisions. |
| core/src/main/java/ai/timefold/solver/core/impl/heuristic/HeuristicConfigPolicy.java | Adds constraint-count field and thread prefix for evolutionary threads. |
| core/src/main/java/ai/timefold/solver/core/impl/phase/AbstractPhase.java | Adds enable/disable logging toggle to phases. |
| core/src/main/java/ai/timefold/solver/core/impl/localsearch/DefaultLocalSearchPhase.java | Respects phase logging toggle for key logs. |
| core/src/main/java/ai/timefold/solver/core/impl/constructionheuristic/DefaultConstructionHeuristicPhase.java | Respects phase logging toggle for step/phase logs. |
| core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/RuinRecreateConstructionHeuristicPhase.java | Disables logging when the ruin-recreate CH phase starts. |
| core/src/main/java/ai/timefold/solver/core/impl/solver/termination/DiminishedReturnsTermination.java | Resets grace-period state on phase end. |
| core/src/main/java/ai/timefold/solver/core/impl/solver/termination/MockablePhaseTermination.java | Makes interface public for use outside the package. |
| core/src/main/java/ai/timefold/solver/core/impl/domain/variable/supply/SupplyManager.java | Adds demand cancellation toggles and cancel-all operation. |
| core/src/main/java/ai/timefold/solver/core/impl/domain/variable/listener/support/VariableListenerSupport.java | Implements new supply manager cancellation semantics. |
| core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/list/SubListSelector.java | Adds min/max sublist size accessors to selector contract. |
| core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/list/RandomSubListSelector.java | Implements the new sublist size accessors. |
| core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/list/mimic/SubListMimicRecorder.java | Adds min/max sublist size accessors to mimic recorder contract. |
| core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/list/mimic/MimicRecordingSubListSelector.java | Delegates new sublist size accessors through recording selector. |
| core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/list/mimic/MimicReplayingSubListSelector.java | Delegates new sublist size accessors through replaying selector. |
| core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/list/SubListSelectorFactory.java | Generalizes nearby-selection wiring to accept any SubListSelector. |
| core/src/main/java/ai/timefold/solver/core/config/heuristic/selector/move/NearbyUtil.java | Adds nearby auto-configuration support for sublist change/swap selectors. |
| core/src/main/java/ai/timefold/solver/core/config/heuristic/selector/move/generic/list/SubListChangeMoveSelectorConfig.java | Enables nearby auto-configuration and config inheritance via inheritConfig. |
| core/src/main/java/ai/timefold/solver/core/config/heuristic/selector/move/generic/list/SubListSwapMoveSelectorConfig.java | Enables nearby auto-configuration and config inheritance via inheritConfig. |
| core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/list/kopt/SelectorBasedTwoOptListMove.java | Adds reverse-tail option and updates describe/rebase behavior. |
| core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/list/kopt/SelectorBasedTwoOptListMoveTest.java | Adds coverage for reverse-tail 2-opt behavior and undo semantics. |
| core/src/test/java/ai/timefold/solver/core/impl/solver/SolverMetricsIT.java | Updates expected move key naming to include reverseTail flag. |
| core/src/test/java/ai/timefold/solver/core/testutil/PlannerTestUtils.java | Extends ScoreDirector mocking to support lookUpWorkingObject delegation. |
| core/src/test/java/ai/timefold/solver/core/testdomain/multivar/TestdataMultiVarEntity.java | Adds a helper to obtain a tertiary variable descriptor. |
| core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/list/RandomSubListSwapMoveSelectorFactoryTest.java | Fixes class name to match file and test focus. |
| core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/list/RandomSubListChangeMoveSelectorFactoryTest.java | Fixes class name to match file and test focus. |
| core/src/test/java/ai/timefold/solver/core/impl/constructionheuristic/placer/entity/QueuedEntityPlacerFactoryTest.java | Updates call signature for entity selector config building. |
| core/src/test/java/ai/timefold/solver/core/config/heuristic/selector/move/MoveSelectorConfigTest.java | Removes sublist move selectors from a disabled-autoconfig assertion list. |
| core/src/test/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/DefaultEvolutionaryAlgorithmPhaseTest.java | Adds integration-level tests validating the new phase can solve basic/list/multivar domains. |
| core/src/test/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/population/individual/generator/DefaultConstructionIndividualStrategyTest.java | Adds unit tests for default construction strategy behavior. |
| core/src/test/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/population/individual/generator/basic/BasicRuinRecreateIndividualStrategyTest.java | Adds unit tests for basic ruin-recreate strategy behavior. |
| core/src/test/java/ai/timefold/solver/core/impl/evolutionaryalgorithm/population/individual/generator/list/ListRuinRecreateIndividualStrategyTest.java | Adds unit tests for list ruin-recreate strategy behavior. |
| core/src/main/java/ai/timefold/solver/core/impl/constructionheuristic/placer/AbstractEntityPlacerFactory.java | Makes change-move selector config builder static for reuse. |
| core/src/main/java/ai/timefold/solver/core/impl/constructionheuristic/placer/QueuedValuePlacerFactory.java | Refactors internal move selector config building method name/signature. |
| core/src/main/java/ai/timefold/solver/core/impl/constructionheuristic/placer/QueuedEntityPlacerFactory.java | Refactors selector config building and default move selector config generation. |
| core/src/main/java/ai/timefold/solver/core/impl/constructionheuristic/DefaultConstructionHeuristicPhaseFactory.java | Adjusts default placer config creation to pass phase config through. |
| core/src/main/java/ai/timefold/solver/core/impl/AbstractFromConfigFactory.java | Adds static overloads to deduce entity descriptors using the config for better errors. |
| core/src/main/java/ai/timefold/solver/core/api/solver/event/EventProducerId.java | Adds evolutionary algorithm phase event producer ID factory method. |
| core/src/main/java/ai/timefold/solver/core/enterprise/TimefoldSolverEnterpriseService.java | Extends enterprise service API for building HGS decider and nearby selection with SubListSelector. |
| core/src/main/java/module-info.java | Exports/opens new evolutionary algorithm packages for module/JAXB use. |
| core/src/build/revapi-differences.json | Updates revapi ignore list for the new JAXB @XmlSeeAlso type. |
| switch (childThreadType) { | ||
| case ChildThreadType.PART_THREAD -> { | ||
| var childThreadScoreDirector = | ||
| scoreDirectorFactory.createScoreDirectorBuilder().withLookUpEnabled(lookUpEnabled) |
| } | ||
| case ChildThreadType.EVOLUTIONARY_AGENT_THREAD, ChildThreadType.MOVE_THREAD -> { | ||
| var childThreadScoreDirector = scoreDirectorFactory.createScoreDirectorBuilder().withLookUpEnabled(true) | ||
| .withConstraintMatchPolicy(constraintMatchPolicy).buildDerived(); | ||
| childThreadScoreDirector.setWorkingSolution(cloneWorkingSolution()); |
| var newSolution = scoreDirector.cloneSolution(solution); | ||
| var newPredecessorAndSuccessorMap = HashMap.<Object, PositionPair> newHashMap(predecessorAndSuccessorMap.size()); | ||
| var chromosomeList = new ArrayList<ChromosomeEntry>(chromosome.length); | ||
| load(scoreDirector.getSolutionDescriptor().getListVariableDescriptor(), planningIdAccessor, solution, chromosomeList, | ||
| newPredecessorAndSuccessorMap); | ||
| var newChromosome = chromosomeList.toArray(ChromosomeEntry[]::new); |
| diff++; | ||
| } | ||
| } | ||
| return (double) diff / (double) predecessorAndSuccessorMap.size(); |
| /** | ||
| * @return the score of the first parent that generated this individual. | ||
| */ | ||
| InnerScore<Score_> getFirstParentScore(); |
| /** | ||
| * @return the score of the second parent that generated this individual. | ||
| */ | ||
| InnerScore<Score_> getSecondParentScore(); |
| * {@link SolutionStateManager#saveSolutionState} captures a snapshot of the current working solution by cloning it | ||
| * and recording which planning values are assigned to each planning entity. | ||
| * <p> |
|
Here are some discussion points I would like assistance with that may not be clear in the review: I tried to simplify the configuration structure, but I'm not happy with that: The current implementation increments the move for each generated individual, but I'm unsure if that's a good idea and would probably need to change. |
|


Implements the Hybrid Genetic Search algorithm as a new solver phase (), based on:
▎ Hybrid Genetic Search for the CVRP: Open-Source Implementation and SWAP* Neighborhood — Thibaut Vidal
What's new
New solver phase: EvolutionaryAlgorithmPhase
A population-based solver phase that runs the HGS loop alongside (or after) existing local search phases. The algorithm operates in three stages:
Support for both variable types
Crossover operators
Adaptive optimization profiles
The
exploratoryRateis auto-selected based on problem scale:Can be overridden via workerConfig.exploratoryRate.
Nearby support for sublist moves
SubListChangeMoveSelectorConfig and SubListSwapMoveSelectorConfig now support nearby selectors, enabling distance-aware move generation in the EA's local search phase.
Performance improvements